{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ввод-вывод средствами С и C++"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Задача**: посчитать сумму 500 000 целых чисел из потока ввода.\n",
    "\n",
    "Решим эту задачу средствами С и С++ и оценим различия в подходах."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сгенерируем тестовые данные:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('input.txt', 'w') as f:\n",
    "    f.write(' '.join(['1'] * 500000))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Решение задачи с использованием средства ввода-вывода С:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load inout/main_c.cpp\n",
    "#include <cstdio>\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tint sum = 0;\n",
    "\tfor (int i = 0; i < 500'000; ++i)\n",
    "\t{\n",
    "\t\tint x = 0;\n",
    "\t\tstd::scanf(\"%i\", &x);\n",
    "\t\tsum += x;\n",
    "\t}\n",
    "\tstd::printf(\"%i\\n\", sum);\n",
    "\treturn 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Со спецификацией форматов можно ознакомиться [здесь](https://en.cppreference.com/w/cpp/io/c/fprintf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Решение задачи с использованием средства ввода-вывода С++:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load inout/main_cpp.cpp\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tint sum = 0;\n",
    "\tfor (int i = 0; i < 500'000; ++i)\n",
    "\t{\n",
    "\t\tint x = 0;\n",
    "\t\tstd::cin >> x;\n",
    "\t\tsum += x;\n",
    "\t}\n",
    "\tstd::cout << sum << std::endl;\n",
    "\treturn 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Скомпилируем решения:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)\r\n",
      "Target: x86_64-pc-linux-gnu\r\n",
      "Thread model: posix\r\n",
      "InstalledDir: /usr/bin\r\n"
     ]
    }
   ],
   "source": [
    "!clang++ --version"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time: 0:00.08\r\n"
     ]
    }
   ],
   "source": [
    "!time -f \"execution time: %E\" clang++ -O3 inout/main_c.cpp -o inout_c.exe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time: 0:00.28\r\n"
     ]
    }
   ],
   "source": [
    "!time -f \"execution time: %E\" clang++ -O3 inout/main_cpp.cpp -o inout_cpp.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проверим размеры исполняемых файлов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-rwxrwxr-x 1 ivafanas ivafanas 8240 июл 29 22:52 inout_c.exe\r\n",
      "-rwxrwxr-x 1 ivafanas ivafanas 8880 июл 29 22:52 inout_cpp.exe\r\n"
     ]
    }
   ],
   "source": [
    "!ls -l *.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Зайдём на godbolt.org и посмотрим на причину, почему исполняемый файл для С++ - решения стал шире"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Запустим, проверим, какое работает быстрее"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time: 0:00.02\r\n",
      "500000\r\n"
     ]
    }
   ],
   "source": [
    "!time -f \"execution time: %E\" cat input.txt | ./inout_c.exe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time: 0:00.05\r\n",
      "500000\r\n"
     ]
    }
   ],
   "source": [
    "!time -f \"execution time: %E\" cat input.txt | ./inout_cpp.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вопросы для обсуждения:\n",
    "* Почему?\n",
    "* Всегда ли решение в стиле С работает быстрее?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Сравнение:\n",
    "\n",
    "С:\n",
    "* быстрая компиляция\n",
    "* быстрое выполнение\n",
    "* меньше размер исходного файла\n",
    "\n",
    "C++:\n",
    "* типобезопасно\n",
    "\n",
    "  ```c++\n",
    "  scanf(**\"%i\"**, &i);\n",
    "  ```\n",
    "\n",
    "\n",
    "* меньше ошибок при множественной записи / чтении:\n",
    "  ```c++\n",
    "  printf(\"%i %f %u\", i, z, n);\n",
    "  std::cout << i << ' ' << z << ' ' << n;\n",
    "  ```\n",
    "\n",
    "* меньше ошибок с адресной арифметикой\n",
    "\n",
    "  ```c++\n",
    "  scanf(\"%i\", **&**i);\n",
    "  ```\n",
    "\n",
    "\n",
    "<br />\n",
    "\n",
    "**Вывод**: если программа упирается в ввод-вывод - используем С - вариант, если нет - С++ - вариант."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup\n",
    "!rm -f *.exe input.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Работа с командной строкой"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Общепринят формат передачи аргументов командной строки в программу, в котором аргументы делятся на:\n",
    "* позиционные\n",
    "* флажки\n",
    "* именованные со значением\n",
    "\n",
    "Пример программы `ls`, выводящей содержимое папки:\n",
    "\n",
    "```\n",
    "ls /usr/bin -a --human-readable --color=always\n",
    "ls -a --color always --human-readable /usr/bin\n",
    "```\n",
    "\n",
    "описание опций можно прочитать, набрав `ls --help`:\n",
    "\n",
    "```\n",
    "  -a, --all                  do not ignore entries starting with .\n",
    "  -h, --human-readable       with -l and/or -s, print human readable sizes\n",
    "                               (e.g., 1K 234M 2G)\n",
    "      --color[=WHEN]         colorize the output; WHEN can be 'always' (default\n",
    "                               if omitted), 'auto', or 'never'; more info below\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Задача**: вывести аргументы командной строки, переданные консольной утилите"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load cmdline/main.cpp\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "int main(int argc, char** argv)\n",
    "{\n",
    "\tstd::cout << \"command line arguments are:\" << std::endl;\n",
    "\tfor (int i = 0; i < argc; ++i)\n",
    "\t{\n",
    "\t\tstd::cout << \"    \" << i << \" -> \" << argv[i] << std::endl;\n",
    "\t}\n",
    "\treturn 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Скомпилируем"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "!clang++ -O3 cmdline/main.cpp -o a.out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Протестируем"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "command line arguments are:\r\n",
      "    0 -> ./a.out\r\n",
      "    1 -> /usr/bin\r\n",
      "    2 -> -a\r\n",
      "    3 -> --human-readable\r\n",
      "    4 -> --color=always\r\n"
     ]
    }
   ],
   "source": [
    "!./a.out /usr/bin -a --human-readable --color=always"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "command line arguments are:\r\n",
      "    0 -> ./a.out\r\n",
      "    1 -> /usr/bin\r\n",
      "    2 -> -a\r\n",
      "    3 -> --human-readable\r\n",
      "    4 -> --color\r\n",
      "    5 -> always\r\n"
     ]
    }
   ],
   "source": [
    "!./a.out /usr/bin -a --human-readable --color always"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "command line arguments are:\r\n",
      "    0 -> ./a.out\r\n",
      "    1 -> /usr/bin/my awesome app dir\r\n",
      "    2 -> -a\r\n",
      "    3 -> --human-readable\r\n",
      "    4 -> --color=always\r\n"
     ]
    }
   ],
   "source": [
    "!./a.out \"/usr/bin/my awesome app dir\" -a --human-readable --color=always"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Приберём мусор"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup\n",
    "!rm -f a.out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Готовые решения:\n",
    "\n",
    "* C++, cross-platform [`boost::program_options`](https://www.boost.org/doc/libs/1_58_0/doc/html/program_options.html)\n",
    "* C, unix-only [`getopt`](https://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Время компиляции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Задача:** посчитать приближение `n`-ой степени числа `e` по формуле `(1 + 1/n)^n`, где `n` вводится пользователем с клавиатуры.\n",
    "\n",
    "Решение данной задачи тривиально, а мы рассмотрим две возможные реализации решения."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вариант 1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex1/util.h\n",
    "#pragma once\n",
    "\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "unsigned read_n(std::istream& is);\n",
    "\n",
    "void write_result(double res, std::ostream& os);\n",
    "\n",
    "double calc_exp(unsigned n);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex1/util.cpp\n",
    "#include \"util.h\"\n",
    "\n",
    "#include <cmath>\n",
    "\n",
    "unsigned read_n(std::istream& is)\n",
    "{\n",
    "\tunsigned rv = 0;\n",
    "\tstd::cin >> rv;\n",
    "\treturn rv;\n",
    "}\n",
    "\n",
    "void write_result(double res, std::ostream& os)\n",
    "{\n",
    "\tos << res << std::endl;\n",
    "}\n",
    "\n",
    "double calc_exp(unsigned n)\n",
    "{\n",
    "\tdouble b = 1 + 1.0 / n;\n",
    "\treturn std::pow(b, n);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex1/solver.h\n",
    "#pragma once\n",
    "\n",
    "#include <iostream>\n",
    "\n",
    "void solve(std::istream& is, std::ostream& os);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex1/solver.cpp\n",
    "#include \"util.h\"\n",
    "\n",
    "\n",
    "void solve(std::istream& is, std::ostream& os)\n",
    "{\n",
    "\tconst unsigned n = read_n(is);\n",
    "\tconst double res = calc_exp(n);\n",
    "\twrite_result(res, os);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex1/main.cpp\n",
    "#include \"solver.h\"\n",
    "\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tsolve(std::cin, std::cout);\n",
    "\treturn 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Вариант 2:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex2/util.h\n",
    "#pragma once\n",
    "\n",
    "#include <iosfwd>\n",
    "\n",
    "\n",
    "unsigned read_n(std::istream& is);\n",
    "\n",
    "void write_result(double res, std::ostream& os);\n",
    "\n",
    "double calc_exp(unsigned n);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex2/util.cpp\n",
    "#include \"util.h\"\n",
    "\n",
    "#include <cmath>\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "unsigned read_n(std::istream& is)\n",
    "{\n",
    "\tunsigned rv = 0;\n",
    "\tis >> rv;\n",
    "\treturn rv;\n",
    "}\n",
    "\n",
    "void write_result(double res, std::ostream& os)\n",
    "{\n",
    "\tos << res << std::endl;\n",
    "}\n",
    "\n",
    "double calc_exp(unsigned n)\n",
    "{\n",
    "\tdouble b = 1 + 1.0 / n;\n",
    "\treturn std::pow(b, n);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex2/solver.h\n",
    "#pragma once\n",
    "\n",
    "#include <iosfwd>\n",
    "\n",
    "void solve(std::istream& is, std::ostream& os);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex2/solver.cpp\n",
    "#include \"util.h\"\n",
    "\n",
    "\n",
    "void solve(std::istream& is, std::ostream& os)\n",
    "{\n",
    "\tconst unsigned n = read_n(is);\n",
    "\tconst double res = calc_exp(n);\n",
    "\twrite_result(res, os);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load exp/ex2/main.cpp\n",
    "#include \"solver.h\"\n",
    "\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tsolve(std::cin, std::cout);\n",
    "\treturn 0;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Измерим время компиляции:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time: 0:00.77\r\n"
     ]
    }
   ],
   "source": [
    "!time -f \"execution time: %E\" clang++ -O3 exp/ex1/main.cpp exp/ex1/solver.cpp exp/ex1/util.cpp -o exponent_1.exe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time: 0:00.57\r\n"
     ]
    }
   ],
   "source": [
    "!time -f \"execution time: %E\" clang++ -O3 exp/ex2/main.cpp exp/ex2/solver.cpp exp/ex2/util.cpp -o exponent_2.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проверим работоспособность:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.70481\n",
      "2.70481\n"
     ]
    }
   ],
   "source": [
    "!echo 100 | ./exponent_1.exe\n",
    "!echo 100 | ./exponent_2.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Почему разница во времени компиляции 200 мс?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# cleanup\n",
    "!rm -f *.exe"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Подробнее про стоимость `#include` можно посмотреть здесь:\n",
    "\n",
    "https://artificial-mind.net/projects/compile-health/"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
